#include "stdafx.h"
#include "platform.hpp"
#include "math/random.hpp"
#include "math/randomstream.hpp"
#include "code/digestengine.hpp"
#include "code/sha1engine.hpp"
using namespace JHCPP::code;
#include <ctime>

#if defined(JH_OS_WINDOWS)
#include <Windows.h>
#include <wincrypt.h>
#elif defined(JH_OS_LINUX)
#include <fcntl.h>
#include <unistd.h>
#endif

namespace JHCPP
{
	namespace math
	{
		///////////////////////////////////////////////////////////////
		//CRandomBuf
		///////////////////////////////////////////////////////////////
		CRandomBuf::CRandomBuf() : BufferedStreamBuf(256, std::ios::in)
		{
		}

		CRandomBuf::~CRandomBuf()
		{
		}

		int CRandomBuf::readFromDevice(char* buffer, std::streamsize length)
		{
			int n = 0;

		#if defined(JH_OS_WINDOWS)
			HCRYPTPROV hProvider = 0;
			::CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
			::CryptGenRandom(hProvider, (DWORD) length, (BYTE*) buffer);
			::CryptReleaseContext(hProvider, 0);
			n = static_cast<int>(length);
		#else
			#if defined(JH_OS_LINUX)
				int fd = open("/dev/urandom", O_RDONLY, 0);
				if (fd >= 0) 
				{
					n = read(fd, buffer, length);
					close(fd);
				}
			#endif
			if (n <= 0)
			{
				// x is here as a source of randomness, so it does not make
				// much sense to protect it with a Mutex.
				static UInt32 x = 0;
				CRandom rnd1(256);
				CRandom rnd2(64);
				x += rnd1.next();

				n = 0;
				CSHA1Engine engine;
				UInt32 t = (UInt32) std::time(NULL);
				engine.update(&t, sizeof(t));
				void* p = this;
				engine.update(&p, sizeof(p));
				engine.update(buffer, length);
				UInt32 junk[32];
				engine.update(junk, sizeof(junk));
				while (n < length)
				{
					for (int i = 0; i < 100; ++i)
					{
						UInt32 r = rnd2.next();
						engine.update(&r, sizeof(r));
						engine.update(&x, sizeof(x));
						x += rnd1.next();
					}
					CDigestEngine::Digest d = engine.digest();
					for (CDigestEngine::Digest::const_iterator it = d.begin(); it != d.end() && n < length; ++it, ++n)
					{
						engine.update(*it);
						*buffer++ = *it++;
					}
				}
			}
			#endif
			return n;
		}


		///////////////////////////////////////////////////////////////
		//CRandomIOS
		///////////////////////////////////////////////////////////////
		CRandomIOS::CRandomIOS()
		{

		}

		CRandomIOS::~CRandomIOS()
		{
		}

		CRandomBuf* CRandomIOS::rdbuf()
		{
			return &m_buf;
		}

		///////////////////////////////////////////////////////////////
		//CRandomBuf
		///////////////////////////////////////////////////////////////
		CRandomInputStream::CRandomInputStream(): std::istream(&m_buf)
		{
		}

		CRandomInputStream::~CRandomInputStream()
		{
		}
	}//end of namespace math
}//end of namespace JHCPP
